use std::{env, fs};
use pest::Parser;
use pest_derive::Parser;
// use strum_macros;
// use strum_macros::{Display, EnumString, EnumIter};
use strum::Display;   // 来自 strum，而不再是 strum_macros

// #[derive(Parser)]
// #[grammar = "lexer.pest"] // 指向你的 .pest 文件
// struct SysYLexer;

fn main() {
    // 收集命令行参数
    let args: Vec<String> = env::args().collect();

    // 检查是否提供了文件名
    if args.len() < 2 {
        eprintln!("Usage: {} <filename>", args[0]);
        std::process::exit(1);
    }

    // 获取文件名
    let filename = &args[1];

    // 读取输入文件
    let input = fs::read_to_string(filename).expect("Failed to read file");

    // 词法分析
    let tokens = tokenize(&input);

    println!("{:?}", tokens);
}

#[derive(Parser)]
#[grammar = "lexer.pest"]
pub struct ExpressionParser;

#[derive(Debug, PartialEq, Display)]
#[strum(serialize_all = "UPPERCASE")]
pub enum Token {
    // key word
    Const, Int, Void, If, Else, While, Break, Continue, Return,
    // op
    Plus, Minus, Mul, Div, Mod, Assign, Eq, Neq, Lt, Gt, Le, Ge, Not, And, Or,
    // delimeter
    LParen, RParen, LBrace, RBrace, LBracket, RBracket, Comma, Semicolon,
    //ident
    Ident(String),
    IntegerConst(i64),
    LineComment(String),
    MultiLineComment(String),
    // EOI
    EOI,
}

// macro_rules! enum_name {
//     ($val: expr) => {
//         stringify!($val).split("::").last().unwrap().to_uppercase()
//     }
// }

// impl Token {
//     pub fn name(&self) -> String {
//         // 应该用过程宏, use strum instead
//         // enum_name!(self)

//     }
// }


pub fn tokenize(input: &str) -> Vec<Token> {
    let pair = ExpressionParser::parse(Rule::file, input)
        .unwrap_or_else(|e| panic!("Parse error: {}", e))
        .next()
        .unwrap(); // 取第一个匹配
    pair.into_inner()
        .flat_map(|p| {
            let mut tokens = vec![];
            match p.as_rule() {
                Rule::token => {
                    for p in p.into_inner() {
                        // println!("1: {:?}", p.as_rule());
                        let token = match p.as_rule() {
                            Rule::key_const => {
                                Token::Const
                            },
                            Rule::key_void => {
                                Token::Void
                            },
                            Rule::key_int => {
                                Token::Int
                            },
                            Rule::EOI => {
                                Token::EOI
                            },
                            Rule::key_if => {
                                Token::If
                            },
                            Rule::key_else => {
                                Token::Else
                            },
                            Rule::key_while => {
                                Token::While
                            },
                            Rule::key_break => {
                                Token::Break
                            },
                            Rule::key_continue => {
                                Token::Continue
                            },
                            Rule::key_return => {
                                Token::Return
                            },
                            Rule::op_plus => {
                                Token::Plus
                            },
                            Rule::op_minus => {
                                Token::Minus
                            },
                            Rule::op_mul => {
                                Token::Mul
                            },
                            Rule::op_div => {
                                Token::Div
                            },
                            Rule::op_mod => {
                                Token::Mod
                            },
                            Rule::op_assign => {
                                Token::Assign
                            },
                            Rule::op_eq => {
                                Token::Eq
                            },
                            Rule::op_neq => {
                                Token::Neq
                            },
                            Rule::op_lt => {
                                Token::Lt
                            },
                            Rule::op_gt => {
                                Token::Gt
                            },
                            Rule::op_le => {
                                Token::Le
                            },
                            Rule::op_ge => {
                                Token::Ge
                            },
                            Rule::op_not => {
                                Token::Not
                            },
                            Rule::op_and => {
                                Token::And
                            },
                            Rule::op_or => {
                                Token::Or
                            },
                            Rule::l_paren => {
                                Token::LParen
                            },
                            Rule::r_paren => {
                                Token::RParen
                            },
                            Rule::l_brace => {
                                Token::LBrace
                            },
                            Rule::r_brace => {
                                Token::RBrace
                            },
                            Rule::l_bracket => {
                                Token::LBracket
                            },
                            Rule::r_bracket => {
                                Token::RBracket
                            },
                            Rule::comma => {
                                Token::Comma
                            },
                            Rule::semicolon => {
                                Token::Semicolon
                            },
                            Rule::ident => {
                                Token::Ident(p.as_str().to_string())
                            },
                            Rule::integer => {
                                // println!("{}", p.as_str());
                                Token::IntegerConst(auto_int(p.as_str()).unwrap())
                            },
                            Rule::line_comment => {
                                Token::LineComment(p.as_str().to_string())
                            },
                            Rule::multiline_comment => {
                                Token::MultiLineComment(p.as_str().to_string())
                            },
                            _ => {
                                unreachable!("token: {:?}", p.as_rule());
                            }
                        };
                        let (token_str, line_num) = (p.as_str().to_string(), p.line_col().0);
                        println!("{} {} at Line {}.", token.to_string(), token_str, line_num);
                        tokens.push(token);
                    }
                },
                Rule::EOI => {
                    // tokens.push(Token::EOI);
                    println!("EOI at Line {}.", p.line_col().0);
                },
                _ => unreachable!("{:?}", p.as_rule()),
            }

            tokens
        })
        .collect()
}



/// 把十六进制字符串 `"0x1"` 或 `"0X1"` 转换成 `i64`。
/// 允许出现前导空白；出现非法字符时返回 `None`。
pub fn hex_to_i64(s: &str) -> Option<i64> {
    let s = s.trim();
    // 去掉可选的 0x / 0X
    let digits = s
        .strip_prefix("0x")
        .or_else(|| s.strip_prefix("0X"))
        .unwrap_or(s);
    i64::from_str_radix(digits, 16).ok()
}

/// 把八进制字符串 `"01"` 转换成 `i64`。
/// 允许前导空白；非法字符返回 `None`。
/// 注意：如果字符串以 `0` 开头但长度 >1，仍按八进制解析；
///       单个 `"0"` 也会被正确解析为 0。
pub fn oct_to_i64(s: &str) -> Option<i64> {
    let s = s.trim();
    // 非空且只能出现 0-7 的数字
    if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit() && b <= b'7') {
        return None;
    }
    i64::from_str_radix(s, 8).ok()
}

/// 自动识别进制并转换为 i64
/// 0x1 / 0X1  -> 16 进制
/// 01 / 077   -> 8  进制
/// 123        -> 10 进制
pub fn auto_int(s: &str) -> Option<i64> {
    let s = s.trim();
    if s.is_empty() {
        return None;
    }

    // 16 进制
    if let Some(digits) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
        return i64::from_str_radix(digits, 16).ok();
    }

    // 8 进制（以 0 开头且长度 > 1）
    if s.starts_with('0') && s.len() > 1 {
        // 快速检查是否全是合法八进制数字
        if s[1..].bytes().all(|b| b >= b'0' && b <= b'7') {
            return i64::from_str_radix(s, 8).ok();
        } else {
            return None;
        }
    }

    // 默认 10 进制
    s.parse::<i64>().ok()
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_auto() {
        assert_eq!(auto_int("0x1"),   Some(1));
        assert_eq!(auto_int("0XFF"),  Some(255));
        assert_eq!(auto_int("01"),    Some(1));
        assert_eq!(auto_int("010"),   Some(8));
        assert_eq!(auto_int("0"),     Some(0));
        assert_eq!(auto_int("123"),   Some(123));
        assert_eq!(auto_int("  0x10 "), Some(16));
        assert_eq!(auto_int("0xGG"),  None);
        assert_eq!(auto_int("08"),    None); // 非法八进制
    }

    #[test]
    fn test_hex() {
        assert_eq!(hex_to_i64("0x1"), Some(1));
        assert_eq!(hex_to_i64("0X1"), Some(1));
        assert_eq!(hex_to_i64("0xFF"), Some(255));
        assert_eq!(hex_to_i64("  0x10  "), Some(16));
        assert_eq!(hex_to_i64("0xGG"), None);
    }

    #[test]
    fn test_oct() {
        assert_eq!(oct_to_i64("01"), Some(1));
        assert_eq!(oct_to_i64("010"), Some(8));
        assert_eq!(oct_to_i64("0"), Some(0));
        assert_eq!(oct_to_i64("  07  "), Some(7));
        assert_eq!(oct_to_i64("08"), None); // 非法八进制
    }
}